package util;

import java.util.ArrayList;

import model.TicTacToeOligoSystemExplicitMix;

import org.apache.commons.math3.ode.events.EventHandler;
import org.apache.commons.math3.util.Pair;

public class MixingEventHandler implements EventHandler {

	int dim; //size of one individual
	int width; // grid size
	int height;
	int offset1; // offset of the w autocatalyst. Doesn't diffuse
	int offset2; // idem for v
	
	public MixingEventHandler(int dim, int width, int height, int offset1, int offset2){
		this.dim = dim;
		this.width = width;
		this.height = height;
		this.offset1 = offset1;
		this.offset2 = offset2;
	}
	
	@Override
	public Action eventOccurred(double arg0, double[] arg1, boolean arg2) {
		return EventHandler.Action.RESET_STATE;
	}

	@Override
	public double g(double t, double[] arg1) {
		double modtime = (t - 0.5) % 2;
		if (modtime < 1){
			return (1-2*modtime);
		}
		return (-3+2*modtime);
	}

	@Override
	public void init(double arg0, double[] arg1, double arg2) {
		// TODO Auto-generated method stub

	}

	@Override
	public void resetState(double time, double[] y) {
		// Explicit mixing of values
		double mask = 0;
		int offset = 0;
		
		for(int i = 0; i < this.width; i++){
			for(int j = 0; j < this.height; j++){
				for(int k = 0; k < dim; k++){
					
					if(k == this.offset1 || k == this.offset2){
						k += 12; //skip
					}
					if(k == this.offset1 || k == this.offset2){
						k += 12; //skip, if they are right behing each other
					}
					// lost from leaks
					mask = - y[offset+k]*((double)getNeighbours(i,j).size());
					for(Pair<Integer,Integer> neighbour : getNeighbours(i,j)){
					 // from the neighbours
					mask += y[(neighbour.getKey()*height+neighbour.getValue())*dim+k];
					}
						mask = Math.rint(mask/TicTacToeOligoSystemExplicitMix.leakThreshold)*TicTacToeOligoSystemExplicitMix.leakThreshold;
						y[offset+k] += TicTacToeOligoSystemExplicitMix.leakFactor*mask; //Only multiply now, to avoid rounding problems: doesn't even slightly help
					
				}
				offset += dim;
			}
		}

	}
	
	private ArrayList<Pair<Integer,Integer>> getNeighbours(int i,int j){
		ArrayList<Pair<Integer,Integer>> neighbours = new ArrayList<Pair<Integer,Integer>>();
		if(i > 0){ // there is a left neighbour
			neighbours.add(new Pair<Integer,Integer>(i-1,j));
		}
		if(i < width - 1){ // there is a right neighbour
			neighbours.add(new Pair<Integer,Integer>(i+1,j));
		}
		if(j > 0){ // there is a up neighbour
			neighbours.add(new Pair<Integer,Integer>(i,j-1));
		}
		if(j < height - 1){ // there is a down neighbour
			neighbours.add(new Pair<Integer,Integer>(i,j+1));
		}
		return neighbours;
	}

}
